﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Xml.Linq;

namespace hotel.Common
{
    /// <summary>
    /// 定义：身份证类 
    ///       密封类，不可继承
    /// 说明：1.提供对身份证认证，
    ///       2.根据提供信息随机生成一个身份证号码
    ///       3.获取身份证信息
    /// 创建者    操作    日期
    /// </summary>
    public sealed class IdentityCard
    {
        private static Hashtable areaInfo = new Hashtable();
        static IdentityCard()
        {
            #region 地址信息
            var datas = from test in XElement.Load("IdentityCardDatas.xml").Elements("data")
                        select new
                        {
                            Key = test.Attribute("key").Value,
                            Value = test.Attribute("value").Value
                        };
            foreach (var item in datas)
            {
                areaInfo.Add(item.Key, item.Value);
            }
            #endregion;
        }
        /// <summary>
        /// 检查身份证号
        /// </summary>
        /// <param name="identityCard">身份证号</param>
        /// <returns></returns>
        public static bool Check(string identityCard)
        {
            if (string.IsNullOrEmpty(identityCard) || (identityCard.Length != 15 && identityCard.Length != 18))
                throw new ArgumentException("身份证号不符合格式");
            bool nRet = false;
            if (identityCard.Length == 18)
                nRet = Check18IdentityCard(identityCard);
            else
                nRet = Check15IdentityCard(identityCard);
            return nRet;
        }

        /// <summary>
        /// 验证18位身份证号
        /// </summary>
        /// <param name="identityCard">The identity card.</param>
        /// <returns></returns>
        private static bool Check18IdentityCard(string identityCard)
        {
            long n = 0;
            //替换身份证最后一位为x的身份证号
            if (!long.TryParse(identityCard.Replace('x', '0').Replace('X', '0'), out n))
                return false;
            //数字验证
            if (n < Math.Pow(10, 16))
                return false;
            //地址验证
            if (!areaInfo.ContainsKey(identityCard.Remove(6)))
                return false;

            //生日验证
            string birth = identityCard.Substring(6, 8).Insert(6, "-").Insert(4, "-");
            DateTime time = new DateTime();
            if (!DateTime.TryParse(birth, out time))
                return false;

            //校验码验证
            string[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(',');
            string[] Wi = ("7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2").Split(',');
            char[] Ai = identityCard.Remove(17).ToCharArray();
            int sum = 0;
            for (int i = 0; i < 17; i++)
            {
                sum += int.Parse(Wi[i]) * int.Parse(Ai[i].ToString());
            }
            int y = -1;
            Math.DivRem(sum, 11, out y);
            if (arrVarifyCode[y] != identityCard.Substring(17, 1).ToLower())
                return false;

            return true;//符合GB11643-1999标准
        }

        /// <summary>
        /// 验证15位身份证号
        /// </summary>
        /// <param name="identityCard">The identity card.</param>
        /// <returns></returns>
        private static bool Check15IdentityCard(string identityCard)
        {
            long n = 0;
            //数字验证
            if (!long.TryParse(identityCard, out n) || n < Math.Pow(10, 14))
                return false;
            //地址验证
            if (areaInfo.ContainsKey(identityCard.Remove(6)))
                return false;
            //生日验证
            string birth = identityCard.Substring(6, 6).Insert(4, "-").Insert(2, "-");
            DateTime time = new DateTime();
            if (DateTime.TryParse(birth, out time) == false)
                return false;
            return true;//符合15位身份证标准
        }

        /// <summary>
        /// 根据身份证获取身份证信息
        /// </summary>
        /// <param name="identityCard">身份证号</param>
        /// <param name="areaCode">6位区域编码</param>
        /// <param name="areaName">区域地址</param>
        /// <param name="sex">性别：0-女，1-男，2-未知</param>
        /// <param name="birthDate">出生日期</param>
        /// <returns>
        /// 0-获取成功，1-身份证号格式不正确
        /// </returns>
        public static int GetInfoByIdentityCard(string identityCard, out string areaCode, out string areaName, out int sex, out DateTime birthDate)
        {
            sex = 2;
            birthDate = new DateTime();
            areaName = string.Empty;
            areaCode = string.Empty;
            bool check = Check(identityCard);
            if (!check)
                return 1;
            areaCode = identityCard.Remove(6);
            areaName = areaInfo[areaCode].ToString();
            string birth = string.Empty;
            if (identityCard.Length == 18)
                birth = identityCard.Substring(6, 8).Insert(6, "-").Insert(4, "-");
            else
                identityCard.Substring(6, 6).Insert(4, "-").Insert(2, "-");
            DateTime.TryParse(birth, out birthDate);
            string sexStr = string.Empty;
            if (identityCard.Length == 18)
                sexStr = identityCard.Substring(14, 3);
            else
                sexStr = identityCard.Substring(12, 3);
            //性别代码为偶数是女性奇数为男性
            if (Int32.Parse(sexStr) % 2 == 0)
                sex = 0;
            else
                sex = 1;
            return 0;
        }

        /// <summary>
        /// 获取自定义身份证号
        /// 主要给测试人员测试使用的身份证号
        /// </summary>
        /// <param name="areaCode">6位区域编码，但区域编码为000000时，默认为自动生成区域编码</param>
        /// <param name="birthDate">出生日期</param>
        /// <param name="sex">性别：0-女，1-男，2-未知</param>
        /// <returns>自定义身份证号</returns>
        public static string GetIdentityCard(string areaCode, DateTime birthDate, int sex)
        {

            if (areaCode.Length != 6)
                throw new ArgumentException("区域编码格式不符，必须是6位。");
            if (!areaInfo.ContainsKey(areaCode) && !"000000".Equals(areaCode))
                throw new ArgumentException("不存在此区域编码");
            string identityCard;
            Random ran = new Random();
            //生成随机区域
            if ("000000".Equals(areaCode))
            {
                int areaNum = 0;
                while (true)
                {
                    areaNum = ran.Next(110000, 659001);
                    if (areaInfo.ContainsKey(areaNum))
                        break;
                }
                areaCode = areaNum.ToString();
            }
            //出生日期
            string birth = birthDate.ToString("yyyyMMdd");
            //3位随机码
            int sexNum = 0;
            while (true)
            {
                sexNum = ran.Next(100, 999);
                if (sex == 0 && sexNum % 2 == 0)//女性，3位码必须是偶数
                    break;
                else if (sex == 1 && sexNum % 2 == 1)//男性，3位码必须是奇数
                    break;
                else if (sex == 2)//未知
                    break;
            }
            identityCard = areaCode + birth + sexNum.ToString();
            //验证码
            string[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(',');
            string[] Wi = ("7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2").Split(',');
            char[] Ai = identityCard.Remove(17).ToCharArray();
            int sum = 0;
            for (int i = 0; i < 17; i++)
            {
                sum += int.Parse(Wi[i]) * int.Parse(Ai[i].ToString());
            }
            int y = -1;
            Math.DivRem(sum, 11, out y);
            string validateStr = arrVarifyCode[y];
            identityCard += validateStr;
            return identityCard;
        }

    }
}
